from typing import Optional

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone


class Mistake(models.Model):
    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        SUBMITTED = "submitted", "Submitted"
        RESOLVED = "resolved", "Resolved"

    student = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="mistakes",
    )
    title = models.CharField(max_length=255)
    question = models.TextField()
    question_image = models.ImageField(
        upload_to="mistakes/questions/%Y/%m/",
        blank=True,
        null=True,
        help_text="上传题目图片（可选）"
    )
    solution_attempt = models.TextField(blank=True)
    solution_attempt_image = models.ImageField(
        upload_to="mistakes/solutions/%Y/%m/",
        blank=True,
        null=True,
        help_text="上传解题过程图片（可选）"
    )
    correct_solution = models.TextField(blank=True)
    reflection = models.TextField(blank=True)
    status = models.CharField(
        max_length=20, choices=Status.choices, default=Status.DRAFT
    )
    submitted_at = models.DateTimeField(null=True, blank=True)
    resolved_at = models.DateTimeField(null=True, blank=True)
    teacher_feedback = models.TextField(blank=True)
    llm_conversation_id = models.CharField(max_length=255, blank=True)
    is_archived = models.BooleanField(default=False)
    archived_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ("-updated_at",)

    def __str__(self) -> str:
        return f"{self.title} ({self.get_status_display()})"

    def get_absolute_url(self):
        from django.urls import reverse

        return reverse("learning:mistake_detail", args=[self.pk])

    @property
    def assigned_teacher(self):
        return getattr(self.student, "assigned_teacher", None)

    def clean(self):
        super().clean()
        if self.student_id:
            if not getattr(self.student, "is_student", False):
                raise ValidationError(
                    {"student": "Mistake entries must belong to a student."}
                )
        if self.status == self.Status.RESOLVED and not self.resolved_at:
            raise ValidationError(
                {"resolved_at": "Resolved mistakes must record resolved_at."}
            )
        if self.is_archived and not self.archived_at:
            raise ValidationError(
                {"archived_at": "Archived mistakes must record archived_at."}
            )
        if not self.is_archived and self.archived_at:
            raise ValidationError(
                {"archived_at": "Active mistakes cannot have archived_at."}
            )

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def submit_for_review(self):
        if self.status != self.Status.DRAFT:
            raise ValueError("Only draft mistakes can be submitted for review.")
        self.status = self.Status.SUBMITTED
        self.submitted_at = timezone.now()
        self.save(update_fields=("status", "submitted_at", "updated_at"))

    def resolve(self, *, reviewer, feedback: str = ""):
        if getattr(reviewer, "is_teacher", False) is False:
            raise PermissionError("Only teachers can resolve a mistake.")
        assigned_teacher_id = getattr(self.student, "assigned_teacher_id", None)
        if assigned_teacher_id != reviewer.id:
            raise PermissionError("Only the assigned teacher can resolve this mistake.")
        self.status = self.Status.RESOLVED
        self.teacher_feedback = feedback
        self.resolved_at = timezone.now()
        self.is_archived = False
        self.archived_at = None
        self.save(
            update_fields=(
                "status",
                "teacher_feedback",
                "resolved_at",
                "is_archived",
                "archived_at",
                "updated_at",
            )
        )

    def record_llm_message(self, *, sender: "MistakeLLMMessage.Sender", content: str):
        return self.llm_messages.create(sender=sender, content=content)

    def archive(self):
        if self.is_archived:
            return
        self.is_archived = True
        self.archived_at = timezone.now()
        self.save(update_fields=("is_archived", "archived_at", "updated_at"))

    def restore(self):
        if not self.is_archived:
            return
        self.is_archived = False
        self.archived_at = None
        self.save(update_fields=("is_archived", "archived_at", "updated_at"))


class MistakeLLMMessage(models.Model):
    class Sender(models.TextChoices):
        STUDENT = "student", "Student"
        TEACHER = "teacher", "Teacher"
        LLM = "llm", "LLM"

    mistake = models.ForeignKey(
        Mistake,
        on_delete=models.CASCADE,
        related_name="llm_messages",
    )
    sender = models.CharField(max_length=20, choices=Sender.choices)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ("created_at",)

    def __str__(self) -> str:
        return f"{self.sender} -> {self.mistake_id}"


class MistakeLLMConversation(models.Model):
    mistake = models.ForeignKey(
        Mistake,
        on_delete=models.CASCADE,
        related_name="llm_conversations",
    )
    conversation_id = models.CharField(max_length=255, unique=True)
    name = models.CharField(max_length=255, blank=True)
    is_active = models.BooleanField(default=True)
    archived_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ("-created_at",)

    def __str__(self) -> str:
        return f"{self.conversation_id} -> {self.mistake_id}"

    def archive(self):
        if not self.is_active:
            return
        self.is_active = False
        self.archived_at = timezone.now()
        self.save(update_fields=("is_active", "archived_at"))


class TeacherAnnouncement(models.Model):
    teacher = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="announcements",
    )
    title = models.CharField(max_length=255)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ("-created_at",)

    def __str__(self) -> str:
        return f"{self.title} ({self.teacher.username})"


class TeacherAssignment(models.Model):
    teacher = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="assignments",
    )
    title = models.CharField(max_length=255)
    description = models.TextField()
    due_date = models.DateField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ("-created_at",)

    def __str__(self) -> str:
        return f"{self.title} ({self.teacher.username})"


class AssignmentSubmission(models.Model):
    assignment = models.ForeignKey(
        TeacherAssignment,
        on_delete=models.CASCADE,
        related_name="submissions",
    )
    student = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="assignment_submissions",
    )
    content = models.TextField(blank=True)
    submitted_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("assignment", "student")
        ordering = ("-submitted_at",)

    def __str__(self) -> str:
        return f"{self.assignment_id}:{self.student_id}"


class NotebookEntry(models.Model):
    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        SUBMITTED = "submitted", "Submitted"
        PUBLIC = "public", "Public"

    student = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="notebook_entries",
    )
    title = models.CharField(max_length=255)
    content = models.TextField()
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.DRAFT,
    )
    related_mistakes = models.ManyToManyField(
        "Mistake",
        blank=True,
        related_name="notebook_entries",
    )
    submitted_at = models.DateTimeField(null=True, blank=True)
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ("-updated_at",)

    def __str__(self) -> str:
        return f"{self.title} ({self.get_status_display()})"

    def get_absolute_url(self):
        from django.urls import reverse

        return reverse("learning:notebook_detail", args=[self.pk])

    def clean(self):
        super().clean()
        if self.student_id and not getattr(self.student, "is_student", False):
            raise ValidationError(
                {"student": "Notebook entries must belong to a student."}
            )

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def submit_for_review(self):
        if self.status == self.Status.PUBLIC:
            raise ValueError("Public entries cannot be resubmitted for review.")
        if self.status == self.Status.SUBMITTED:
            return
        self.status = self.Status.SUBMITTED
        self.submitted_at = timezone.now()
        self.save(update_fields=("status", "submitted_at", "updated_at"))

    def publish(self, *, reviewer):
        if getattr(reviewer, "is_teacher", False) is False:
            raise PermissionError("Only teachers can publish notebook entries.")
        self.status = self.Status.PUBLIC
        self.published_at = timezone.now()
        self.save(update_fields=("status", "published_at", "updated_at"))

    def revert_to_draft(self, *, reviewer=None):
        if reviewer is not None and getattr(reviewer, "is_teacher", False) is False:
            raise PermissionError("Only teachers can revert submitted entries.")
        self.status = self.Status.DRAFT
        self.submitted_at = None
        self.published_at = None
        self.save(update_fields=("status", "submitted_at", "published_at", "updated_at"))


class NotebookAttachment(models.Model):
    entry = models.ForeignKey(
        NotebookEntry,
        on_delete=models.CASCADE,
        related_name="attachments",
    )
    image = models.ImageField(upload_to="notebook/attachments/%Y/%m/")
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ("-uploaded_at",)

    def __str__(self) -> str:
        return f"Attachment {self.pk} for {self.entry_id}"


class TopicTraining(models.Model):
    """专题训练集合"""
    title = models.CharField(max_length=255, verbose_name="专题名称")
    description = models.TextField(blank=True, verbose_name="专题描述")
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="created_trainings",
        null=True,
        blank=True,
        verbose_name="创建者"
    )
    is_active = models.BooleanField(default=True, verbose_name="是否启用")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ("-created_at",)
        verbose_name = "专题训练"
        verbose_name_plural = "专题训练"

    def __str__(self) -> str:
        return self.title

    def get_absolute_url(self):
        from django.urls import reverse
        return reverse("learning:topic_training_start", args=[self.pk])


class TopicQuestion(models.Model):
    """专题训练中的题目"""
    training = models.ForeignKey(
        TopicTraining,
        on_delete=models.CASCADE,
        related_name="questions",
        verbose_name="所属专题"
    )
    question_number = models.IntegerField(verbose_name="题目序号")
    question_text = models.TextField(verbose_name="题目内容")
    question_image = models.ImageField(
        upload_to="topic_training/questions/%Y/%m/",
        blank=True,
        null=True,
        verbose_name="题目图片"
    )
    correct_answer = models.TextField(verbose_name="正确答案")
    explanation = models.TextField(blank=True, verbose_name="解析")
    # 跳题逻辑：答对后跳转到的题目编号
    next_question_if_correct = models.IntegerField(
        null=True,
        blank=True,
        verbose_name="答对后跳转题号"
    )
    # 答错后跳转到的题目编号
    next_question_if_wrong = models.IntegerField(
        null=True,
        blank=True,
        verbose_name="答错后跳转题号"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ("training", "question_number")
        unique_together = ("training", "question_number")
        verbose_name = "训练题目"
        verbose_name_plural = "训练题目"

    def __str__(self) -> str:
        return f"{self.training.title} - 第{self.question_number}题"


class TopicProgress(models.Model):
    """学生在专题训练中的进度"""
    student = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="topic_progress",
        verbose_name="学生"
    )
    training = models.ForeignKey(
        TopicTraining,
        on_delete=models.CASCADE,
        related_name="student_progress",
        verbose_name="专题"
    )
    current_question = models.ForeignKey(
        TopicQuestion,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="current_students",
        verbose_name="当前题目"
    )
    # 学生的答题记录（JSON格式存储）
    # 格式：[{"question_id": 1, "answer": "...", "is_correct": true, "timestamp": "..."}]
    answer_history = models.JSONField(default=list, verbose_name="答题历史")
    llm_conversation_id = models.CharField(
        max_length=255,
        blank=True,
        verbose_name="AI会话ID"
    )
    is_completed = models.BooleanField(default=False, verbose_name="是否完成")
    started_at = models.DateTimeField(auto_now_add=True, verbose_name="开始时间")
    completed_at = models.DateTimeField(null=True, blank=True, verbose_name="完成时间")
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("student", "training")
        ordering = ("-updated_at",)
        verbose_name = "训练进度"
        verbose_name_plural = "训练进度"

    def __str__(self) -> str:
        return f"{self.student.username} - {self.training.title}"

    def get_next_question(self, is_correct: bool) -> Optional[TopicQuestion]:
        """根据答题正确性获取下一题"""
        if not self.current_question:
            return None
        
        next_num = (
            self.current_question.next_question_if_correct if is_correct
            else self.current_question.next_question_if_wrong
        )
        
        if next_num is None:
            # 没有下一题，训练完成
            return None
        
        try:
            return TopicQuestion.objects.get(
                training=self.training,
                question_number=next_num
            )
        except TopicQuestion.DoesNotExist:
            return None

    def record_answer(self, question: TopicQuestion, answer: str, is_correct: bool, has_image: bool = False):
        """记录答题"""
        record = {
            "question_id": question.id,
            "question_number": question.question_number,
            "answer": answer,
            "is_correct": is_correct,
            "timestamp": timezone.now().isoformat(),
        }
        if has_image:
            record["has_image"] = True
        self.answer_history.append(record)
        self.save(update_fields=["answer_history", "updated_at"])
